package com.mikelouis.test.scala.proginscala.chapter14

/**
 * Created by yuazhu on 1/12/2015.
 */
object Element2 {

  private class ArrayElement2(val contents: Array[String]) extends Element2

  private class LineElement2(s: String) extends Element2 {
    val contents = Array(s)

    override def width = s.length

    override def height = 1
  }

  private class UniformElement2(
                                 ch: Char,
                                 override val width: Int,
                                 override val height: Int
                                 ) extends Element2 {
    private val line = ch.toString * width

    def contents = Array("Hello", "World") // Array.make(height, line)
  }

  def elem(contents: Array[String]): Element2 =
    new ArrayElement2(contents)

  def elem(chr: Char, width: Int, height: Int): Element2 =
    new UniformElement2(chr, width, height)

  def elem(line: String): Element2 =
    new LineElement2(line)
}

import Element2.elem

abstract class Element2 {
  def contents: Array[String]

  def width: Int = contents(0).length

  def height: Int = contents.length

  /**
   *
   * @param that
   * @return
   */
  def above(that: Element2): Element2 = {
    val this1 = this widen that.width
    val that1 = that widen this.width

    /** add assertion */
    assert(this1.width == that1.width)
    // ++操作符串连两个数组
    elem(this1.contents ++ that1.contents)
  }

  /**
   * 把两个元素靠在一起，我们将创造一个新的元素，其中的每一行都来自于两个元素的相应行的串连
   * @param that
   * @return
   */
  def beside(that: Element2): Element2 = {
    val this1 = this heighten that.height
    val that1 = that heighten this.height
    elem(
      // zip方法从它的两个参数中拣出相应的元素并组织成对子数组
      /** 等价于 */
      // val contents = new Array[String](this.contents.length)
      // for (i <- 0 until this.contents.length)
      // contents(i) = this.contents(i) + that.contents(i)
      // new ArrayElement(contents)
      for ((line1, line2) <- this1.contents zip that1.contents)
      yield line1 + line2)
  }

  def widen(w: Int): Element2 = {
    if (w <= width) this
    else {
      val left = elem(' ', (w - width) / 2, height)
      var right = elem(' ', w - width - left.width, height)
      left beside this beside right
    } ensuring (w <= _.width) // 在返回结果值之前，检查一下宽度是否正确。 首先把结果存在val里，然后对这个结果执行断言，并且如果断言成功，则返回val值
  }

  def heighten(h: Int): Element2 =
    if (h <= height) this
    else {
      val top = elem(' ', width, (h - height) / 2)
      var bot = elem(' ', width, h - height - top.height)
      top above this above bot
    }

  override def toString = contents mkString "\n"
}

object LayoutElement2Impl {
  def main(args: Array[String]) {

    println("example [\n" + example + "\n]")
  }

  def example = {
    val column1 = elem("hello") above elem("***")
    val column2 = elem("***") above elem("world")
    column1 beside column2
  }
}